package com.dayatang.hrm.compensation.service.impl;

import java.rmi.RemoteException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.rules.InvalidRuleSessionException;
import javax.rules.StatelessRuleSession;

import org.apache.tools.ant.util.DateUtils;
import org.slf4j.helpers.MessageFormatter;
import org.springframework.context.support.MessageSourceAccessor;
import org.springframework.util.Assert;

import com.dayatang.commons.domain.InstanceFactory;
import com.dayatang.commons.domain.Value;
import com.dayatang.hrm.compensation.CompensationConstant;
import com.dayatang.hrm.compensation.domain.CalculateRule;
import com.dayatang.hrm.compensation.domain.FiscalPeriod;
import com.dayatang.hrm.compensation.domain.PayRoll;
import com.dayatang.hrm.compensation.domain.SalaryAccountSuite;
import com.dayatang.hrm.compensation.domain.SalaryArchive;
import com.dayatang.hrm.compensation.domain.SalaryCalculateItem;
import com.dayatang.hrm.compensation.domain.SalaryItem;
import com.dayatang.hrm.compensation.domain.SalaryItemArchive;
import com.dayatang.hrm.compensation.domain.exception.CompensationRuntimeException;
import com.dayatang.hrm.compensation.service.RuleTemplate;
import com.dayatang.hrm.compensation.service.CompensationCalculateService;
import com.dayatang.hrm.compensation.utils.ValueConvertor;
import com.dayatang.hrm.compensation.vo.RealValue;
import com.dayatang.hrm.staffing.domain.Employee;
import com.dayatang.koala.service.RuleManagement;

@SuppressWarnings("unchecked")
public class CompensationCalculateServiceImpl implements
		CompensationCalculateService {

	public static String RULE_DRL_ENCODING = CompensationConstant.RULE_DRL_ENCODING;

	public static String LINE_SEPARATOR = "\n";

	public static String DATE_FORMAT_FULL_PATTERN = CompensationConstant.DATE_FORMAT_FULL_PATTERN;

	/**
	 * 
	 */
	private static final long serialVersionUID = -6577954877188257757L;

	protected static MessageSourceAccessor messages;

	protected static RuleManagement ruleManagement;

	protected static RuleTemplate ruleTemplate;

	public static MessageSourceAccessor getMessages() {
		return messages;
	}

	public static void setMessages(MessageSourceAccessor messages) {
		CompensationCalculateServiceImpl.messages = messages;
	}

	public static RuleManagement getRuleManagement() {
		if (ruleManagement == null) {
			ruleManagement = InstanceFactory.getInstance(RuleManagement.class);
		}
		return ruleManagement;
	}

	public static void setRuleManagement(RuleManagement ruleManagement) {
		CompensationCalculateServiceImpl.ruleManagement = ruleManagement;
	}

	public static RuleTemplate getRuleTemplate() {
		if (ruleTemplate == null) {
			ruleTemplate = InstanceFactory
					.getInstance(RuleTemplate.class);
		}
		return ruleTemplate;
	}

	public static void setRuleTemplate(RuleTemplate ruleTemplate) {
		CompensationCalculateServiceImpl.ruleTemplate = ruleTemplate;
	}

	@Override
	public PayRoll creatPayRoll(SalaryAccountSuite salaryAccountSuite,
			FiscalPeriod fiscalPeriod) {

		if (fiscalPeriod.isClosed()) {
			throw new CompensationRuntimeException(
					messages
							.getMessage(
									"CompensationCalculateServiceImpl.creatPayRoll.fiscalIsClosed",
									"该会计期间已经关闭。"));
		}

		if (PayRoll.isAlreadyExist(salaryAccountSuite, fiscalPeriod)) {
			throw new CompensationRuntimeException(
					messages
							.getMessage(
									"CompensationCalculateServiceImpl.creatPayRoll.payRollAlreadyExist",
									"该薪资帐套在此会计期间下已存在薪水册。"));
		}

		Date now = new Date();

		List<Employee> employees = salaryAccountSuite.getEmployees(now);
		Assert.notEmpty(employees, messages.getMessage(
				"CompensationCalculateServiceImpl.creatPayRoll.noEmployees",
				"该薪资帐套下还未分配员工。"));

		// 新建薪水册
		PayRoll payRoll = new PayRoll(salaryAccountSuite, fiscalPeriod, now);
		payRoll.save();

		Map<Employee, List<Object>> empParamsMap = new HashMap<Employee, List<Object>>();

		for (Employee employee : employees) {
			empParamsMap.put(employee, new ArrayList<Object>());
		}

		Map<Employee, List<SalaryItemArchive>> empSalaryItemArchiveMap = calculateEmployeeSalaryItemArchivesInPayRoll(
				payRoll, false, empParamsMap);

		for (Employee employee : employees) {
			// 为每个员工生成薪资档案
			SalaryArchive archive = new SalaryArchive(employee, payRoll, now);

			archive.setSalaryItemArchives(new HashSet<SalaryItemArchive>(
					empSalaryItemArchiveMap.get(employee)));

			archive.save();
		}

		return payRoll;
	}

	// @Override
	// public Value calculateSalaryCalculateItem(Employee employee,
	// FiscalPeriod fiscalPeriod, SalaryCalculateItem calItem,
	// Date calculateDate) {
	//
	// List params = assembleParams(employee, fiscalPeriod);
	//
	// String ruleString = assembleRuleString(calItem, calculateDate);
	//
	// Map result = executeRules(ruleString, new HashMap(), params);
	//
	// return fetchValueFromResultMap(calItem, result);
	//
	// }

	@Override
	public Map<Employee, List<SalaryItemArchive>> calculateEmployeeSalaryItemArchivesInPayRoll(
			PayRoll payRoll, boolean validateEmployee,
			Map<Employee, List<Object>> empParamsMap) {

		Map<Employee, List<SalaryItemArchive>> employeeSalaryItemMap = new HashMap<Employee, List<SalaryItemArchive>>();

		SalaryAccountSuite salaryAccountSuite = payRoll.getSalaryAccountSuite();
		Set<Employee> employeesBeCalculated = empParamsMap.keySet();

		if (validateEmployee) {
			if (!salaryAccountSuite.containEmployees(employeesBeCalculated,
					payRoll.getCreated())) {
				throw new CompensationRuntimeException(
						messages
								.getMessage(
										"员工不属于该薪水册。",
										"CompensationCalculateServiceImpl.calculateEmployeeSalaryItemsInPayRoll.empNotBelongToPayRoll"));
			}
		}

		// 薪水册所要包含的薪资项目
		List<SalaryItem> salaryItems = salaryAccountSuite
				.getSalaryItems(payRoll.getCreated());

		// 组装规则
		Map globalMap = new HashMap();

		StatelessRuleSession statelessRuleSession = assembleStatelessRuleSession(
				assembleRuleString4SalaryAccountSuite(salaryAccountSuite,
						payRoll.getCreated()), globalMap, payRoll.getCreated());

		// 对每个员工进行计算
		for (Employee employee : employeesBeCalculated) {

			List<SalaryItemArchive> salaryItemArchives = new ArrayList<SalaryItemArchive>();

			List params = assembleParams(employee, payRoll.getFiscalPeriod());
			// 导入初始的参数
			params.addAll(empParamsMap.get(employee));

			// map.put(key, value) key=薪资项目 value=薪资计算项目的值 （Object）
			Map result = executeRules(statelessRuleSession, globalMap, params);

			for (SalaryItem salaryItem : salaryItems) {

				SalaryItemArchive salaryItemArchive = new SalaryItemArchive(
						salaryItem);
				salaryItemArchive.setValue(fetchValueFromResultMap(salaryItem,
						result));

				salaryItemArchives.add(salaryItemArchive);
			}

			employeeSalaryItemMap.put(employee, salaryItemArchives);

		}

		// 做版本标记：薪资帐套
		salaryAccountSuite.markArchiveAll();// set boolean值 保存

		return employeeSalaryItemMap;
	}

	// private

	private StatelessRuleSession assembleStatelessRuleSession(
			String ruleString, Map globalMap, Date calculateDate) {
		Map map = new HashMap();
		map.put("map", globalMap);
		map.put("calculateDate", calculateDate);
		StatelessRuleSession ruleSession = ruleManagement
				.assembleStatelessRuleSession(ruleString, null, map);
		return ruleSession;
	}

	/*
	 * private Map executeRules(String ruleString, Map globalMap, List params) {
	 * 
	 * globalMap.clear();
	 * 
	 * StatelessRuleSession statelessRuleSession = assembleStatelessRuleSession(
	 * ruleString, globalMap); try { statelessRuleSession.executeRules(params);
	 * } catch (InvalidRuleSessionException e) { e.printStackTrace(); throw new
	 * CompensationRuntimeException(e); } catch (RemoteException e) {
	 * e.printStackTrace(); throw new CompensationRuntimeException(e); }
	 * 
	 * // map.put(key, value) key=薪资计算项目 value=薪资计算项目的值（Object） return
	 * globalMap;
	 * 
	 * }
	 */

	private Map executeRules(StatelessRuleSession statelessRuleSession,
			Map globalMap, List params) {

		globalMap.clear();

		try {
			statelessRuleSession.executeRules(params);
		} catch (InvalidRuleSessionException e) {
			e.printStackTrace();
			throw new CompensationRuntimeException(e);
		} catch (RemoteException e) {
			e.printStackTrace();
			throw new CompensationRuntimeException(e);
		}

		// map.put(key, value) key=薪资计算项目 value=薪资计算项目的值（Object）
		return globalMap;

	}

	/**
	 * 把规则计算出来的Object值，转换成Value对象
	 * 
	 * @param calItem
	 * @param result
	 * @return
	 */
	private Value fetchValueFromResultMap(SalaryCalculateItem calItem,
			Map result) {
		// map.put(key, value) key=薪资计算项目 value=薪资计算项目的值（Object）
		if (result == null || result.isEmpty()) {
			return ValueConvertor.conventToValue(RealValue.valueOf(calItem
					.getDefaultValue()));
		} else {
			return ValueConvertor.conventToValue(RealValue.valueOf(result
					.get(calItem)));
		}

	}

	// private String assembleRuleString(SalaryCalculateItem calItem,
	// Date calculateDate) {
	// return null;
	// }

	private String assembleRuleString4SalaryAccountSuite(
			SalaryAccountSuite salaryAccountSuite, Date queryDate) {

		List<SalaryCalculateItem> salaryCalculateItems = salaryAccountSuite
				.getSalaryCalculateItems(queryDate);

		StringBuffer ruleString = new StringBuffer();

		StringBuffer ruleHeader = new StringBuffer(getCalculateRuleHeader());

		// 组装规则文件头
		ruleString.append(ruleHeader);

		// 组装每个薪资计算项目的规则
		for (SalaryCalculateItem salaryCalculateItem : salaryCalculateItems) {
			writeRuleLine(ruleString, assembleRuleString4SalaryCalculateItem(
					salaryCalculateItem, queryDate));

		}

		return ruleString.toString();

	}

	private String assembleRuleString4SalaryCalculateItem(
			SalaryCalculateItem salaryCalculateItem, Date queryDate) {

		StringBuffer itemRule = new StringBuffer();

		List<CalculateRule> rules = salaryCalculateItem
				.getCalculateRules(queryDate);

		// 组装每个CalculateRule的规则
		for (CalculateRule rule : rules) {
			writeRuleLine(itemRule, getNormalCalculateRule(rule));
		}

		// 组装当薪资计算项目已存在时的规则
		writeRuleLine(itemRule,
				getSalaryCalculateItemDefaultExistRule(salaryCalculateItem));

		// 组装当薪资计算项目不存在时的规则
		writeRuleLine(itemRule,
				getSalaryCalculateItemDefaultNoExistRule(salaryCalculateItem));

		return itemRule.toString();

	}
	
	private List assembleParams(Employee employee, FiscalPeriod fiscalPeriod) {
		// TODO Auto-generated method stub
		return null;
	}

	

	private void writeRuleLine(StringBuffer ruleString, String ruleName) {
		ruleString.append(LINE_SEPARATOR);
		ruleString.append(ruleName);
	}
	
	

	/*
	 * 
	 * 从规则模板组装规则
	 * 
	 */
	private String getSalaryCalculateItemDefaultExistRule(
			SalaryCalculateItem salaryCalculateItem) {

		String salaryCalculateItemName = salaryCalculateItem.getName();
		Long salaryCalculateItemId = salaryCalculateItem.getId();

		return MessageFormatter.arrayFormat(getRuleTemplate()
				.getSalaryCalculateItemDefaultExistRule(), new Object[] {
				salaryCalculateItemName, salaryCalculateItemId });

	}

	private String getSalaryCalculateItemDefaultNoExistRule(
			SalaryCalculateItem salaryCalculateItem) {

		String salaryCalculateItemName = salaryCalculateItem.getName();
		Long salaryCalculateItemId = salaryCalculateItem.getId();

		return MessageFormatter.arrayFormat(getRuleTemplate()
				.getSalaryCalculateItemDefaultNoExistRule(), new Object[] {
				salaryCalculateItemName, salaryCalculateItemId,
				salaryCalculateItemId });

	}
	
	private String getNormalCalculateRule(CalculateRule rule) {

		// 规则名称
		String ruleName = rule.getCalItem().getName()
				+ DateUtils.format(rule.getCreated(), DATE_FORMAT_FULL_PATTERN);
		Long salaryCalculateItemId = rule.getCalItem().getId();
		// 规则条件
		String ruleCondition = rule.getConditionText();
		// 规则结果
		String ruleConsequence = rule.getConsequenceText();

		String normalCalculateRuleTemplate = getRuleTemplate()
				.getNormalCalculateRule();

		return MessageFormatter.arrayFormat(normalCalculateRuleTemplate,
				new Object[] { ruleName, salaryCalculateItemId, ruleCondition,
						salaryCalculateItemId, ruleConsequence });

	}

	private String getCalculateRuleHeader() {
		return getRuleTemplate().getCalculateRuleHeader();
	}

	
}
